---
title: 'Calendar configuration'
description: 'Learn how to configure the Schedule-X calendar'
---

# Calendar configuration

This page describes a few of the config-options available. Some options, however, require more
in-depth understanding and are therefore documented in separate pages.

## Options in the calendar config

```ts
import { createCalendar, viewMonthGrid } from '@schedule-x/calendar'
import '@schedule-x/theme-default/dist/index.css'

const config = {
  // ... views, events and other options

  /**
   * Set the language. List of supported languages: https://schedule-x.dev/docs/calendar/language
   * For support of further languages, please open a PR, adding your translations under the folder:
   * packages/translations/src/locales/xx-XX
   *
   * Defaults to 'en-US'
   * */
  locale: 'zh-CN',

  /**
   * Set the timezone.
   * Defaults to 'UTC'
   * */
  timezone: 'Asia/Tokyo',

  /**
   * Set which day is to be considered the starting day of the week.
   * Follows Temporal API for Gregorian calendar: 1 = Monday, 2 = Tuesday, (...other days) 7 = Sunday
   * Defaults to 1 (Monday)
   * */
  firstDayOfWeek: 7, // 7 = Sunday

  /**
   * The preferred view to display when the calendar is first rendered.
   * all views that you import have a "name" property, which helps you identify them.
   * Defaults to the first view in the "views" array
   * */
  defaultView: viewMonthGrid.name,

  /**
   * The default date to display when the calendar is first rendered. Only accepts Temporal.PlainDate format.
   * Defaults to the current date
   * */
  selectedDate: Temporal.PlainDate.from('2023-12-24'),

  /**
   * Render the calendar in dark mode.
   * Defaults to false
   * */
  isDark: true,

  /**
   * Decides which hours should be displayed in the week and day grids. Only full hours are allowed; 01:30, for example, is not allowed.
   * Defaults to midnight - midnight (a full day)
   * Can also be set to a "hybrid" day, such as { start: '06:00', end: '03:00' }, meaning each day starts at 6am but
   * extends into the next day until 3am.
   * */
  dayBoundaries: {
    start: '06:00',
    end: '18:00',
  },

  /**
   * The minimum and maximum date that can be displayed
   * */
  minDate: Temporal.PlainDate.from('2024-01-01'),
  maxDate: Temporal.PlainDate.from('2024-12-31'),

  weekOptions: {
    /**
     * The total height in px of the week grid (week- and day views)
     * */
    gridHeight: 2500,

    /**
     * The number of days to display in week view
     */
    nDays: 5,

    /**
     * The width in percentage of the event element in the week grid
     * Defaults to 100, but can be used to leave a small margin to the right of the event
     */
    eventWidth: 95,

    /**
     * Intl.DateTimeFormatOptions used to format the hour labels on the time axis
     * Default: { hour: 'numeric' }
     */
    timeAxisFormatOptions: { hour: '2-digit', minute: '2-digit' },

    /**
     * Determines whether concurrent events can overlap.
     * Defaults to true. Set to false to disable overlapping.
     */
    eventOverlap: true,

    /**
     * The granularity of the time grid in minutes for week and day views.
     * Can be 60 (hourly), 30 (half-hourly), or 15 (quarter-hourly).
     * When set to 30 or 15, the timeAxisFormatOptions will automatically include minutes.
     * Defaults to 60
     */
    gridStep: 30,
  },

  monthGridOptions: {
    /**
     * Number of events to display in a day cell before the "+ N events" button is shown
     * */
    nEventsPerDay: 8,
  },

  /**
    * Display week numbers. Not 100% according to ISO 8601, which considers a week to start on Monday and end on Sunday.
    * Since Schedule-X enables you to configure the first day of the week, the week numbers are calculated based on that.
    * */
  showWeekNumbers: true,

  /**
   * Toggle automatic view change when the calendar is resized below a certain width breakpoint.
   * Defaults to true
   * */
  isResponsive: false,

  /**
   * Skip validating events when initializing the calendar. This can help you gain a bit of performance if you are loading a lot of events,
   * and you are sure that the events are valid.
   * */
  skipValidation: true,

  /**
   * Callbacks for events that occur in the calendar
   * */
  callbacks: {
    /**
     * Is called when:
     * 1. Selecting a date in the date picker
     * 2. Selecting a new view
     * */
    onRangeUpdate(range) {
      console.log('new calendar range start date', range.start)
      console.log('new calendar range end date', range.end)
    },

    /**
     * Fetch events for the given date range.
     * This callback is called every time onRangeUpdate runs, and also once on render.
     * The returned events will be converted to internal events and set as $app.calendarEvents.list.value
     * */
    async fetchEvents(range) {
      const events = await fetch(`/api/events?start=${range.start}&end=${range.end}`)
        .then((res) => res.json())
      return events
    },

    /**
     * Is called when an event is updated through drag and drop, resizing or the interactive event modal
     * */
    onEventUpdate(updatedEvent) {
      console.log('onEventUpdate', updatedEvent)
    },

    /**
      * Is called before an event is updated by drag & drop or resizing.
      * If you return false, the update will be aborted,
      * and the event will be reset to its original position.
      * If you return true, the event will be updated.
      * */
    onBeforeEventUpdate(oldEvent, newEvent, $app) {
      // run your validation or side effects
      return false
    },

    // See docs for "onBeforeEventUpdate"
    async onBeforeEventUpdateAsync(oldEvent, newEvent, $app) {
      // run some async validation, like fetching data from an API and then validating
      return false
    },

    /**
     * Is called when an event is clicked
     * */
    onEventClick(calendarEvent, e: UIEvent) {
      console.log('onEventClick', calendarEvent)
    },

    /**
     * Is called when an event is double clicked
     * */
    onDoubleClickEvent(calendarEvent, e: UIEvent) {
      console.log('onDoubleClickEvent', calendarEvent)
    },

    /**
     * Is called when clicking a date in the month grid
     * */
    onClickDate(date: Temporal.PlainDate, e?: UIEvent) {
      console.log('onClickDate', date) // e.g. 2024-01-01
    },

    /**
     * Is called when clicking somewhere in the time grid of a week or day view
     * */
    onClickDateTime(dateTime: Temporal.ZonedDateTime, e?: UIEvent) {
      console.log('onClickDateTime', dateTime) // e.g. 2024-01-01 12:37
    },

    /**
     * Is called when selecting a day in the month agenda
     * */
    onClickAgendaDate(date: Temporal.PlainDate, e?: UIEvent) {
      console.log('onClickAgendaDate', date) // e.g. 2024-01-01
    },

    /**
     * Is called when double-clicking a day in the month agenda
     * */
    onDoubleClickAgendaDate(date: Temporal.PlainDate, e?: UIEvent) {
      console.log('onDoubleClickAgendaDate', date) // e.g. 2024-01-01
    },

    /**
     * Is called when double-clicking a date in the month grid
     * */
    onDoubleClickDate(date: Temporal.PlainDate, e?: UIEvent) {
      console.log('onClickDate', date) // e.g. 2024-01-01
    },

    /**
     * Is called when double-clicking somewhere in the time grid of a week or day view
     * */
    onDoubleClickDateTime(dateTime: Temporal.ZonedDateTime, e?: UIEvent) {
      console.log('onDoubleClickDateTime', dateTime) // e.g. 2024-01-01 12:37
    },

    /**
     * Is called when clicking the "+ N events" button of a month grid-day
     * */
    onClickPlusEvents(date: Temporal.PlainDate, e?: UIEvent) {
      console.log('onClickPlusEvents', date) // e.g. 2024-01-01
    },

    /**
     * Is called when the selected date is updated
     * */
    onSelectedDateUpdate(date: Temporal.PlainDate) {
      console.log('onSelectedDateUpdate', date)
    },

    /**
     * Runs on resizing the calendar, to decide if the calendar should be small or not.
     * This in turn affects what views are rendered.
     * */
    isCalendarSmall($app) {
      return $app.elements.calendarWrapper?.clientWidth! < 500
    },

    /**
     * Runs before the calendar is rendered
     * */
    beforeRender($app) {
      const range = $app.calendarState.range.value
      fetchYourEventsFor(range.start, range.end)
    },

    /**
     * Runs after the calendar is rendered
     * */
    onRender($app) {
      console.log('onRender', $app)
    },
  },
}

const calendar = createCalendar(config)

calendar.render(document.getElementById('calendar'))
```
